<?
    /**
     * PrincipioLab
     *
     * @package    PrincipioLab
     */

    /**
     * PrincipioLab_Date
     *
     * @package    PrincipioLab
     */
    class PrincipioLab_Date extends DateTime
    {
        const MONTH_JANUARY = 1;
        const MONTH_FEBRUARY = 2;
        const MONTH_MARCH = 3;
        const MONTH_APRIL = 4;
        const MONTH_MAY = 5;
        const MONTH_JUNE = 6;
        const MONTH_JULY = 7;
        const MONTH_AUGUST = 8;
        const MONTH_SEPTEMBER = 9;
        const MONTH_OCTOBER = 10;
        const MONTH_NOVEMBER = 11;
        const MONTH_DECEMBER = 12;


        public static $MONTHS_RUSSIAN = array(
            self::MONTH_JANUARY => "январь",
            self::MONTH_FEBRUARY => "февраль",
            self::MONTH_MARCH => "март",
            self::MONTH_APRIL => "апрель",
            self::MONTH_MAY => "май",
            self::MONTH_JUNE => "июнь",
            self::MONTH_JULY => "июль",
            self::MONTH_AUGUST => "август",
            self::MONTH_SEPTEMBER => "сентябрь",
            self::MONTH_OCTOBER => "октябрь",
            self::MONTH_NOVEMBER => "ноябрь",
            self::MONTH_DECEMBER => "декабрь"
        );


        public static $MONTHS_DECLENSION_RUSSIAN = array(
            self::MONTH_JANUARY => "января",
            self::MONTH_FEBRUARY => "февраля",
            self::MONTH_MARCH => "марта",
            self::MONTH_APRIL => "апреля",
            self::MONTH_MAY => "мая",
            self::MONTH_JUNE => "июня",
            self::MONTH_JULY => "июля",
            self::MONTH_AUGUST => "августа",
            self::MONTH_SEPTEMBER => "сентября",
            self::MONTH_OCTOBER => "октября",
            self::MONTH_NOVEMBER => "ноября",
            self::MONTH_DECEMBER => "декабря"
        );


        const DATETIME_BLANK = "1980-01-01 00:00:00";
        const DATETIME_NOW = "now";

        public function __construct($datetime = self::DATETIME_BLANK)
        {
            parent::__construct();

            $this->setDateTime($datetime);
        }


        /**
         *
         * @param DateTime $date_time
         *
         * @return PrincipioLab_Date
         */
        private function _setDateTime(DateTime $date_time)
        {
            $this->setTimestamp($date_time->getTimestamp());
            return $this;
        }


        /**
         * Construct object from any valid source.
         *
         * @param $obj DateTime|PrincipioLab_Date|mixed
         *
         * @return PrincipioLab_Date
         */
        public function setDateTime($obj)
        {
            if ($obj instanceof DateTime)
                return $this->_setDateTime($obj);
            elseif ($obj instanceof PrincipioLab_Date)
                return $this->_setDateTime($obj);
            elseif ($obj == "")
                return $this->_setDateTime(new DateTime(self::DATETIME_BLANK));
            else
                return $this->_setDateTime(new DateTime($obj));
        }

        /**
         * Set current DateTime.
         *
         * @return PrincipioLab_Date
         */
        public function setDateTimeNow() { return $this->setDateTime(self::DATETIME_NOW); }


        /**
         * Set empty DateTime
         *
         * @return PrincipioLab_Date
         */
        public function setDateTimeBlank() { return $this->setDateTime(self::DATETIME_BLANK); }


        /**
         * Check if current DateTime is blank.
         *
         * @return bool
         */
        public function isBlank()
        {
            return (($this->getYear() == 1980) && ($this->getMonth() == 1) && ($this->getDay() == 1) && ($this->getHour() == 0) && ($this->getMinute() == 0) && ($this->getSecond() == 0));
        }


        /**
         * @return int Year
         */
        public function getYear() { return intval($this->format("Y")); }


        /**
         * @return int Month
         */
        public function getMonth() { return intval($this->format("m")); }


        /**
         * @return int Day
         */
        public function getDay() { return intval($this->format("d")); }


        /**
         * @return int Hour
         */
        public function getHour() { return intval($this->format("H")); }


        /**
         * @return int Minute
         */
        public function getMinute() { return intval($this->format("i")); }


        /**
         * @return int Second
         */
        public function getSecond() { return intval($this->format("s")); }


        /**
         * @return int Microsecond
         */
        public function getMicroSecond() { return intval($this->format("u")); }


        /**
         * Return DateTime in a SQL format YYYY-MM-DD HH:MM:SS.
         *
         * @return string
         */
        public function toSQLFormat()
        {
            return
                $this->getYear() . "-" .
                PrincipioLab_Tools::addZero($this->getMonth()) . "-" .
                PrincipioLab_Tools::addZero($this->getDay()) . " " .
                PrincipioLab_Tools::addZero($this->getHour()) . ":" .
                PrincipioLab_Tools::addZero($this->getMinute()) . ":" .
                PrincipioLab_Tools::addZero($this->getSecond());
        }


        /**
         * Return DateTime in a human declension.
         *
         * @return string
         */
        public function toHumanDeclension()
        {
            if ($this->isBlank())
                return "-";
            else
                return PrincipioLab_Tools::addZero($this->getDay()) .
                    " " .
                    (self::$MONTHS_DECLENSION_RUSSIAN[intval($this->getMonth())]) .
                    " " .
                    $this->getYear() .
                    "&nbsp;&nbsp;&nbsp;" .
                    PrincipioLab_Tools::addZero($this->getHour()) .
                    ":" .
                    PrincipioLab_Tools::addZero($this->getMinute()) .
                    ":" .
                    PrincipioLab_Tools::addZero($this->getSecond());
        }


        /**
         * Subtract given DateTime from this.
         *
         * @param PrincipioLab_Date $date
         * @param bool              $absolute
         *
         * @return PrincipioLab_DateInterval
         * @throws RuntimeException
         */
        public function diffWith(PrincipioLab_Date $date, $absolute = true)
        {
            if ($this->isBlank())
                throw new RuntimeException("'left' date is blank.");

            if ($date->isBlank())
                throw new RuntimeException("'right' date is blank.");

            return new PrincipioLab_DateInterval($this->diff($date, $absolute));
        }


        /**
         * Subtract this DateTime from given.
         *
         * @param PrincipioLab_Date $date
         * @param bool              $absolute
         *
         * @return PrincipioLab_DateInterval
         * @throws RuntimeException
         */
        public function diffFrom(PrincipioLab_Date $date, $absolute = true)
        {
            if ($this->isBlank())
                throw new RuntimeException("'right' date is blank.");

            if ($date->isBlank())
                throw new RuntimeException("'left' date is blank.");

            return new PrincipioLab_DateInterval($date->diff($this, $absolute));
        }


        /**
         * Subtract NOW from this DateTime.
         *
         * @param bool $absolute
         *
         * @return PrincipioLab_DateInterval
         */
        public function diffWithNow($absolute = true)
        {
            return $this->diffWith(PrincipioLab_Date::getNow(), $absolute);
        }


        /**
         * Subtract this DateTime from NOW.
         *
         * @param bool $absolute
         *
         * @return PrincipioLab_DateInterval
         */
        public function diffFromNow($absolute = true)
        {
            return $this->diffFrom(PrincipioLab_Date::getNow(), $absolute);
        }


        public function diffToTimestamp(PrincipioLab_Date $date) { return self::doDiffToTimestamp($this, $date); }


        public function diffToTime(PrincipioLab_Date $date)
        {
            if ($this->isBlank())
                throw new RuntimeException("'left' date is blank");
            else
                return self::doDiffToTime($this, $date);
        }


        public static function makeTimeFromTimestamp($timestamp)
        {
            $hours = floor($timestamp / 3600);
            $minutes = floor(($timestamp % 3600) / 60);
            $seconds = floor($timestamp - ($hours * 3600) - ($minutes * 60));

            return PrincipioLab_Tools::addZero($hours) . ":" . PrincipioLab_Tools::addZero($minutes) . ":" . PrincipioLab_Tools::addZero($seconds);
        }


        public static function doDiffToTimestamp(PrincipioLab_Date $date_1, PrincipioLab_Date $date_2)
        {
            if ($date_1->isBlank())
                throw new RuntimeException("'right' date is blank.");

            if ($date_2->isBlank())
                throw new RuntimeException("'left' date is blank.");

            return ($date_1->getTimestamp() - $date_2->getTimestamp());
        }


        public static function doDiffToTime(PrincipioLab_Date $date_1, PrincipioLab_Date $date_2)
        {
            return PrincipioLab_Date::makeTimeFromTimestamp(PrincipioLab_Date::doDiffToTimestamp($date_1, $date_2));
        }


        /**
         * @return PrincipioLab_Date Current DateTime
         */
        public static function getNow() { return new PrincipioLab_Date(self::DATETIME_NOW); }


        /**
         * @return PrincipioLab_Date Blank DateTime
         */
        public static function getBlank() { return new PrincipioLab_Date(self::DATETIME_BLANK); }
    }
?>